import { ChangeDetectorRef, Component, Input, TemplateRef } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { IconModule } from '../icon/icon.module';
import { SwitchModule } from './switch.module';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { ChangeDetectionStrategy } from '@angular/compiler';

@Component({
  template: `
    <lv-switch
      [lvSize]="lvSize"
      [lvLoading]="lvLoading"
      [lvControl]="lvControl"
      [lvLabelOn]="lvLabelOn"
      [lvLabelOff]="lvLabelOff"
      [lvDisabled]="lvDisabled"
      [(ngModel)]="value"
      (ngModelChange)="onChange()"
    ></lv-switch>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestSwitchComponent {
  @Input() lvSize: 'default' | 'large' = 'default';
  @Input() lvLoading: boolean = false;
  @Input() lvDisabled: boolean = false;
  @Input() lvControl: boolean = false;
  @Input() lvLabelOn: string | TemplateRef<void>;
  @Input() lvLabelOff: string | TemplateRef<void>;

  value = false;

  onChange() {}
}

describe('SwitchComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestSwitchComponent],
      imports: [SwitchModule, IconModule, FormsModule, NoopAnimationsModule],
    }).compileComponents();
  }));

  describe('render', () => {
    let component: TestSwitchComponent;
    let fixture: ComponentFixture<TestSwitchComponent>;
    let cdr: ChangeDetectorRef;
    const renderComponent = (param?) => {
      if (param) {
        for (const [key, value] of Object.entries(param)) {
          component[key] = value;
        }
      }
      tick(500);
      cdr.detectChanges();
      expect(fixture).toMatchSnapshot();
      component.value = true;
      tick(500);
      cdr.detectChanges();
      tick(500);
      cdr.detectChanges();
      expect(fixture).toMatchSnapshot();
    };

    beforeEach(() => {
      fixture = TestBed.createComponent(TestSwitchComponent);
      component = fixture.componentInstance;
      cdr = fixture.debugElement.injector.get(ChangeDetectorRef);
    });

    it('with default', fakeAsync(() => {
      renderComponent();
    }));

    it('with lvSize large', fakeAsync(() => {
      renderComponent({ lvSize: 'large' });
    }));

    it('with lvLoading', fakeAsync(() => {
      renderComponent({ lvLoading: true });
    }));

    it('with lvDisabled', fakeAsync(() => {
      renderComponent({ lvLoading: true });
    }));

    it('with lvLabelOn | lvLabelOff', fakeAsync(() => {
      renderComponent({ lvLabelOn: 'ON', lvLabelOff: 'OFF' });
    }));
  });

  describe('work', () => {
    let component: TestSwitchComponent;
    let fixture: ComponentFixture<TestSwitchComponent>;

    const toggle = () => {
      const el = fixture.nativeElement.querySelector('.lv-switch-host > div');
      el.dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
    };

    beforeEach(() => {
      fixture = TestBed.createComponent(TestSwitchComponent);
      component = fixture.componentInstance;
    });

    it('should work when click', fakeAsync(() => {
      jest.spyOn(component, 'onChange');
      fixture.detectChanges();
      const el = fixture.nativeElement.querySelector('.lv-switch-host > div');
      el.dispatchEvent(new Event('click'));
      tick(500);
      fixture.detectChanges();
      expect(component.onChange).toHaveBeenCalled();
      expect(component.value).toEqual(true);
    }));

    it('should not clickable with lvControl', fakeAsync(() => {
      component.lvControl = true;
      fixture.detectChanges();
      toggle();
      expect(component.value).toEqual(false);
    }));

    it('should not clickable when disabled', fakeAsync(() => {
      component.lvDisabled = true;
      fixture.detectChanges();
      toggle();
      expect(component.value).toEqual(false);
    }));

    it('should not clickable when loading', fakeAsync(() => {
      component.lvLoading = true;
      fixture.detectChanges();
      toggle();
      expect(component.value).toEqual(false);
    }));
  });
});
